﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using Elderos.AI;
using Elderos.Articles;
using Elderos.Highlight;
using Elderos.Highlight.Search;
using Elderos.Utils.Logging;
using Elderos.Utils.Serialization;

namespace AccordTrainer
{
    public class TrainingSetManager
    {
        private readonly string _articlesString = ConfigurationManager.ConnectionStrings["Articles"].ConnectionString;
        private ArticlesData _articles;
        public IHighlighter Highlighter { get; set; }
        public int DegreeOfParallelism { get; set; }

        private int _countDone = 0;

        public TrainingSetManager()
        {
            InitializeArticlesManager();
            
            Highlighter = new WebHighlighter("endpoint");
        }

        public string[] GetFactorNames()
        {
            var article = _articles.GetArticleAsync(1).Result;
            var highlightResult = Highlighter.Highlight(article.ToHighlightableString());
            var names = highlightResult.Positions[0].EntitiesFlattened[0].FactorValues.OrderBy(x => x.FactorName).Select(x => x.FactorName);
            return names.ToArray();
        }

        public IEnumerable<TrainItem<ItemInfo>> GetTrainingSet()
        {
            _countDone = 0;

            List<int> articleIDs = null;
            while (articleIDs == null)
            {
                try
                {
                    Logger.Info("Trying to get ideal articles...");
                    articleIDs = _articles.GetIdealArticles();
                    Logger.Info("Ideal articles loaded.");
                }
                catch (Exception ex)
                {
                    Logger.Error("Error getting ideal articles: " + ex.Message);
                }
            }
            
            var result = articleIDs
                .AsParallel()
                .WithDegreeOfParallelism(DegreeOfParallelism > 0 ? DegreeOfParallelism : Environment.ProcessorCount)
                .Select(id =>
                            {
                                try
                                {
                                    return _articles.GetArticleAsync(id).Result;
                                }
                                catch
                                {
                                }
                                return null;
                            })
                .Where(x => x != null)
                .SelectMany(GetArticleTrainItems)
                .ToArray();

            return result;
        }

        private void InitializeArticlesManager()
        {
            _articles = new ArticlesData(_articlesString);
        }

        private static int _entityInfoCounter = 0;

        private IEnumerable<TrainItem<ItemInfo>> GetArticleTrainItems(Article article)
        {
            const double winnerOutput = 0.8;
            const double loserOutput = -0.8;

            var idealHighlight = XSerializer.DeserializeObject<ArticleHighlight>(article.IdealHighlight.ToString(),
                                                                                     typeof(DataContractSerializer));

            List<Position> entityInfos = null;

            while (true)
            {
                try
                {
                    var highlightResult = Highlighter.Highlight(article.ToHighlightableString());
                    entityInfos = highlightResult.Positions == null
                                      ? null
                                      : highlightResult.Positions.SelectMany(x => x.SubpositionsFlattened).ToList();
                    break;
                }
                catch(TimeoutException)
                {
                    Logger.Info("Article {0} Timeout exceeded.", article.ID);
                }
                catch (Exception ex)
                {
                    Logger.Info(ex.ToString());
                }
            }

            if (entityInfos == null)
            {
                yield return null;
            }
            else
            {
                foreach (var position in entityInfos)
                {
                    var i = Interlocked.Increment(ref _entityInfoCounter);
                    var interval = position.CharInterval;
                    var correspondent = idealHighlight.Winners.FirstOrDefault(x => x.Position.Equals(interval));

                    foreach (var entity in position.Entities)
                    {
                        double output;
                        if (correspondent == null)
                            output = loserOutput;
                        else if (correspondent.WinnerID == entity.EntityID)
                            output = winnerOutput;
                        else
                            output = loserOutput;
                        var trainItem = new TrainItem<ItemInfo>();
                        trainItem.Inputs = entity.FactorVector;
                        trainItem.Output = output;
                        trainItem.AdditionalInfo = new ItemInfo()
                                                       {
                                                           ArticleID = article.ID,
                                                           PositionID = i,
                                                           EntityID = entity.EntityID,
                                                           PositionText = position.Text
                                                       };

                        yield return trainItem;
                    }
                }
            }

            var count = Interlocked.Increment(ref _countDone);
            Logger.Info(count + " articles done.");
        }
    }
    
}
